﻿Option Explicit On
Option Strict On

Imports System
Imports System.Collections
Imports System.Globalization

#Region " ConfigKeyEventArgs Class "

Public Class ConfigKeyEventArgs
    Inherits EventArgs

#Region " Private Members "

    Private m_keyName As String = Nothing
    Private m_keyValue As String = Nothing

#End Region

#Region " Constructor "

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="keyName"></param>
    ''' <param name="keyValue"></param>
    ''' <remarks></remarks>
    Public Sub New(keyName As String, keyValue As String)
        Me.m_keyName = keyName
        Me.m_keyValue = keyValue
    End Sub

#End Region

#Region " Public Properties "

    Public ReadOnly Property KeyName() As String
        Get
            Return m_keyName
        End Get
    End Property

    Public ReadOnly Property KeyValue() As String
        Get
            Return m_keyValue
        End Get
    End Property

#End Region

End Class

#End Region

Public Class ConfigBase
    Implements IConfig

#Region " Private Members "

    Private m_configName As String = Nothing
    Private m_configSource As IConfigSource = Nothing
    Private m_aliasText As AliasText = Nothing
    Private m_format As IFormatProvider = NumberFormatInfo.CurrentInfo
    Private m_Keys As New Generic.Dictionary(Of Object, Object)

#End Region

#Region " Public Events "

    Public Event KeySet As EventHandler(Of ConfigKeyEventArgs) Implements IConfig.KeySet
    Public Event KeyRemoved As EventHandler(Of ConfigKeyEventArgs) Implements IConfig.KeyRemoved

#End Region

#Region " Constructors "

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="name"></param>
    ''' <param name="source"></param>
    ''' <remarks></remarks>
    Public Sub New(name As String, source As IConfigSource)
        m_configName = name
        m_configSource = source
        m_aliasText = New AliasText()
    End Sub

#End Region

#Region " Public Properties "

    Public Property Name() As String Implements IConfig.Name
        Get
            Return m_configName
        End Get
        Set(value As String)
            If m_configName <> value Then
                Rename(value)
            End If
        End Set
    End Property

    Public ReadOnly Property ConfigSource() As IConfigSource Implements IConfig.ConfigSource
        Get
            Return m_configSource
        End Get
    End Property

    Public ReadOnly Property Aliases() As AliasText Implements IConfig.Aliases
        Get
            Return m_aliasText
        End Get
    End Property

#End Region

#Region " Public Methods "

    Public Function Contains(key As String) As Boolean Implements IConfig.Contains
        Return (GetValue(key) IsNot Nothing)
    End Function

    Public Overridable Function GetValue(key As String) As String Implements IConfig.GetValue
        Dim result As String = Nothing

        If m_Keys.ContainsKey(key) Then
            result = m_Keys(key).ToString()
        End If

        Return result
    End Function

    Public Function GetValue(key As String, defaultValue As String) As String Implements IConfig.GetValue
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, result)
    End Function

    Public Function GetExpanded(key As String) As String Implements IConfig.GetExpanded
        Return Me.ConfigSource.GetExpanded(Me, key)
    End Function

    Public Function GetString(key As String) As String Implements IConfig.GetString
        Return GetValue(key)
    End Function

    Public Function GetString(key As String, defaultValue As String) As String Implements IConfig.GetString
        Return GetValue(key, defaultValue)
    End Function

    Public Function GetInt32(key As String) As Integer Implements IConfig.GetInt32
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return Convert.ToInt32(text, m_format)
    End Function

    Public Function GetInt32(key As String, fromAlias As Boolean) As Integer Implements IConfig.GetInt32
        If Not fromAlias Then
            Return GetInt32(key)
        End If

        Dim result As String = GetValue(key)

        If result Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return GetIntAlias(key, result)
    End Function

    Public Function GetInt32(key As String, defaultValue As Integer) As Integer Implements IConfig.GetInt32
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, Convert.ToInt32(result, m_format))
    End Function

    Public Function GetInt32(key As String, defaultValue As Integer, fromAlias As Boolean) As Integer Implements IConfig.GetInt32
        If Not fromAlias Then
            Return GetInt32(key, defaultValue)
        End If

        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, GetIntAlias(key, result))
    End Function

    Public Function GetInt64(key As String) As Long Implements IConfig.GetInt64
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return Convert.ToInt64(text, m_format)
    End Function

    Public Function GetInt64(key As String, defaultValue As Long) As Long Implements IConfig.GetInt64
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, Convert.ToInt64(result, m_format))
    End Function

    Public Function GetBoolean(key As String) As Boolean Implements IConfig.GetBoolean
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return GetBooleanAlias(text)
    End Function

    Public Function GetBoolean(key As String, defaultValue As Boolean) As Boolean Implements IConfig.GetBoolean
        Dim text As String = GetValue(key)

        Return If((text Is Nothing), defaultValue, GetBooleanAlias(text))
    End Function

    Public Function GetSingle(key As String) As Single Implements IConfig.GetSingle
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return Convert.ToSingle(text, m_format)
    End Function

    Public Function GetSingle(key As String, defaultValue As Single) As Single Implements IConfig.GetSingle
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, Convert.ToSingle(result, m_format))
    End Function

    Public Function GetDouble(key As String) As Double Implements IConfig.GetDouble
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return Convert.ToDouble(text, m_format)
    End Function

    Public Function GetDouble(key As String, defaultValue As Double) As Double Implements IConfig.GetDouble
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, Convert.ToDouble(result, m_format))
    End Function

    Public Function GetDecimal(key As String) As Decimal Implements IConfig.GetDecimal
        Dim text As String = GetValue(key)

        If text Is Nothing Then
            Throw New ArgumentException("Value not found: " + key)
        End If

        Return Convert.ToDecimal(text, m_format)
    End Function

    Public Function GetDecimal(key As String, defaultValue As Decimal) As Decimal Implements IConfig.GetDecimal
        Dim result As String = GetValue(key)

        Return If((result Is Nothing), defaultValue, Convert.ToDecimal(result, m_format))
    End Function

    Public Function GetKeys() As String() Implements IConfig.GetKeys
        Dim result As String() = New String(m_Keys.Keys.Count - 1) {}

        m_Keys.Keys.CopyTo(result, 0)

        Return result
    End Function

    Public Function GetValues() As String() Implements IConfig.GetValues
        Dim result As String() = New String(m_Keys.Values.Count - 1) {}

        m_Keys.Values.CopyTo(result, 0)

        Return result
    End Function

    Public Sub Add(key As String, value As String)
        m_Keys.Add(key, value)
    End Sub

    Public Overridable Sub SetValue(key As String, value As Object) Implements IConfig.SetValue
        If value Is Nothing Then
            Throw New ArgumentNullException("value")
        End If

        If GetValue(key) Is Nothing Then
            Me.Add(key, value.ToString())
        Else
            m_Keys(key) = value.ToString()
        End If

        If ConfigSource.AutoSave Then
            ConfigSource.Save()
        End If

        OnKeySet(New ConfigKeyEventArgs(key, value.ToString()))
    End Sub

    Public Overridable Sub Remove(key As String) Implements IConfig.Remove
        If key Is Nothing Then
            Throw New ArgumentNullException("key")
        End If

        If GetValue(key) IsNot Nothing Then
            Dim keyValue As String = Nothing
            If KeySetEvent IsNot Nothing Then
                keyValue = GetValue(key)
            End If
            m_Keys.Remove(key)

            OnKeyRemoved(New ConfigKeyEventArgs(key, keyValue))
        End If
    End Sub

#End Region

#Region " Protected Methods "

    Protected Sub OnKeySet(e As ConfigKeyEventArgs)
        RaiseEvent KeySet(Me, e)
    End Sub

    Protected Sub OnKeyRemoved(e As ConfigKeyEventArgs)
        RaiseEvent KeyRemoved(Me, e)
    End Sub

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Renames the config to the new name. 
    ''' </summary>
    ''' <param name="name"></param>
    ''' <remarks></remarks>
    Private Sub Rename(name As String)
        Me.ConfigSource.Configs.Remove(Me)
        m_configName = name
        Me.ConfigSource.Configs.Add(Me)
    End Sub

    ''' <summary>
    ''' Returns the integer alias first from this IConfig then 
    ''' the parent if there is none.
    ''' </summary>
    ''' <param name="key"></param>
    ''' <param name="alias"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetIntAlias(key As String, [alias] As String) As Integer
        Dim result As Integer = -1

        If m_aliasText.ContainsInt(key, [alias]) Then
            result = m_aliasText.GetInt32(key, [alias])
        Else
            result = ConfigSource.Aliases.GetInt32(key, [alias])
        End If

        Return result
    End Function

    ''' <summary>
    ''' Returns the boolean alias first from this IConfig then 
    ''' the parent if there is none.
    ''' </summary>
    ''' <param name="key"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetBooleanAlias(key As String) As Boolean
        Dim result As Boolean = False

        If m_aliasText.ContainsBoolean(key) Then
            result = m_aliasText.GetBoolean(key)
        Else
            If ConfigSource.Aliases.ContainsBoolean(key) Then
                result = ConfigSource.Aliases.GetBoolean(key)
            Else
                Throw New ArgumentException("Alias value not found: " + key + ". Add it to the Alias property.")
            End If
        End If

        Return result
    End Function

#End Region

End Class
